<template>
	<view class="xl-image-content" :style="{ opacity: Number(opacity), borderRadius: borderRadius, transition: `opacity ${time / 1000}s ease-in-out`}" :class="'xl-image-lazy-item-' + elIndex">
		<view :class="'xl-image-lazy-item-' + elIndex" style="position: relative;">
			<view class="loading-container" v-if="(!isShow || loading) && !isError">
				<image class="loading" src="@/uni_modules/xingluo-ui/static/imgs/img/loading.gif" mode="aspectFit" @load="imgLoaded"></image>
			</view>
      <block v-if="stop">
        <image v-if="!isError&&src" :style="{borderRadius: $xl.parseUnit(borderRadius, 'rpx'), width: $xl.calcUnit(width, 'rpx'), height: $xl.calcUnit(height, 'rpx')}" class="xl-image-lazy-item" :src="isShow ? src : ''" :mode="mode" @load="imgLoaded" @error="loadError" @click.stop="clickImg"></image>
        <image v-else :style="{borderRadius: borderRadius, width: $xl.calcUnit(width, 'rpx'), height: $xl.calcUnit(height, 'rpx'),'background':'#fff'}" class="xl-image-lazy-item" src="@/uni_modules/xingluo-ui/static/imgs/img/error.png" :mode="mode" @load="errorImgLoaded" @click.stop="clickImg"></image>
      </block>
      <block v-else>
        <image v-if="!isError && src" :style="{borderRadius: $xl.parseUnit(borderRadius, 'rpx'), width: $xl.calcUnit(width, 'rpx'), height: $xl.calcUnit(height, 'rpx')}" class="xl-image-lazy-item" :src="isShow ? src : ''" :mode="mode" @load="imgLoaded" @error="loadError" @click="clickImg"></image>
        <image v-else :style="{borderRadius: borderRadius, width: $xl.calcUnit(width, 'rpx'), height: $xl.calcUnit(height, 'rpx'),'background':'#fff'}" class="xl-image-lazy-item" src="@/uni_modules/xingluo-ui/static/imgs/img/error.png" :mode="mode" @load="errorImgLoaded" @click="clickImg"></image>
      </block>
		</view>
	</view>
</template>
<script setup>
	
	import { ref, computed, watch, nextTick, onMounted, getCurrentInstance } from 'vue'
	// 组件传参
	import propsData from './props.js'
	const props = defineProps(propsData)
	const emits = defineEmits(['click', 'load', 'error', 'finish'])
	
	const elIndex = ref(uni.$xl.guid())
	
	let isShow = ref(false)
	let loading = ref(true)
	
	// 将threshold从rpx转为px
	let getThreshold = computed(()=>{
		// 先取绝对值，因为threshold可能是负数，最后根据props.threshold是正数或者负数，重新还原
		let thresholdPx = uni.$xl.upx2px(Math.abs(props.threshold));
		return props.threshold < 0 ? -thresholdPx : thresholdPx;
	})
	
	const instance = getCurrentInstance(); 
	onMounted(() => {
		// 由父组件生命周期onReachBottom发出，目的是让页面到底时，保证所有图片都进行加载，做到绝对稳定且可靠
		nextTick(() => {
			uni.$once('onReachBottom', () => {
				console.log('触底监听')
				if (!isShow.value) isShow.value = true;
			});
		})
		// mounted的时候，不一定挂载了这个元素，延时30ms，否则会报错或者不报错，但是也没有效果
		setTimeout(() => {
			// 创建一个交叉观察器
			const contentObserver = uni.createIntersectionObserver(instance, {
				nativeMode: true			// 启用小程序原生的 IntersectionObserver。
			});
			contentObserver.relativeToViewport({
				bottom: getThreshold.value,
			}).observe('.xl-image-lazy-item-' + elIndex.value, (res) => {
				if (res.intersectionRatio > 0) {
					// 懒加载状态改变
					isShow.value = true;
					// 如果图片已经加载，去掉监听，减少性能的消耗
					contentObserver.disconnect();
				}
			})
		}, 30)
	})
	
	
	let time = ref(props.duration)
	let opacity = ref(1)
	watch( () => isShow.value, (newValue, oldValue) => {
		// 如果是不开启过渡效果，直接返回
		if (!props.isEffect) return;
		time.value = 0;
		// 原来opacity为1(不透明，是为了显示占位图)，改成0(透明，意味着该元素显示的是背景颜色，默认的白色)，再改成1，是为了获得过渡效果
		opacity.value = 0;
		// 延时30ms，否则在浏览器H5，过渡效果无效
		setTimeout(() => {
			time.value = props.duration;
			opacity.value = 1;
		}, 30)
	})
	
	let isError = ref(false) // 图片加载失败
	let loadStatus = ref('') // 默认是懒加载中的状态
	// 图片路径发生变化时，需要重新标记一些变量，否则会一直卡在某一个状态，比如isError
	watch( () => props.src, (newValue, oldValue) => {
		if (!newValue) {
			// 如果传入null或者''，或者undefined，标记为错误状态
			isError.value = true;
		} else {
			isError.value = false;
		}
	}, {immediate: true})
	
	// 点击图片触发的事件,loadlazy-还是懒加载中状态，loading-图片正在加载，loaded-图片加加载完成
	function clickImg(e) {
		let whichImg = '';
		// 如果isShow为false，意味着图片还没开始加载，点击的只能是最开始的占位图
		if (!isShow.value) whichImg = 'lazyImg';
		// 如果isError为true，意味着图片加载失败，这是只剩下错误的占位图，所以点击的只能是错误占位图
		// 当然，也可以给错误的占位图元素绑定点击事件，看你喜欢~
		else if (isError.value) whichImg = 'errorImg';
		// 总共三张图片，除了两个占位图，剩下的只能是正常的那张图片了
		else whichImg = 'realImg';
		// 只通知当前图片的index
		emits('click');
	}

	// 图片加载完成事件，可能是加载占位图时触发，也可能是加载真正的图片完成时触发，通过isShow区分
	function imgLoaded(e) {
		// 占位图加载完成
		if (loadStatus.value === '') {
			loadStatus.value = 'lazyed';
		}
		// 真正的图片加载完成 
		else if (loadStatus.value === 'lazyed') {
			loadStatus.value = 'loaded';
			emits('load', e);
			// 成功与否都传出,看情况使用哪个
			emits('finish', e);
			setTimeout(() => {
				loading.value = false;
			}, 100)
		}
	}
	
	// 错误的图片加载完成
	function errorImgLoaded(e) {
		emits('error', e);
		// 成功与否都传出,看情况使用哪个
		emits('finish', e);
	}
	
	// 图片加载失败
	function loadError() {
		isError.value = true;
	}
</script>
<style lang="scss" scoped>
	.xl-image-content {
		overflow: hidden;
	}
	.loading-container {
		width: 240rpx;
		height: 240rpx;
		position: absolute;
		top: 50%;
		left: 50%;
		right: 0;
		z-index: 9;
		transform: translate(-50%, -50%);
		.loading {
			width: 100%;
			height: 100%;
			// animation: move 1.2s linear infinite;
		}
	}
	.xl-image-lazy-item {
		width: 100%;
		// 骗系统开启硬件加速
		transform: transition3d(0, 0, 0);
		// 防止图片加载“闪一下”
		will-change: transform;
		/* #ifndef APP-NVUE */
		display: block;
		/* #endif */
	}
	@keyframes move {
		from {
			transform: rotate(0);
		}
		to {
			transform: rotate(360deg);
		}
	}
</style>